#pragma once

#include <iostream>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <string>
#include <cassert>
#include <unistd.h>

using std::cerr;
using std::cout;
using std::endl;

const std::string gpathname = "/home/ddsm/_11_25_systemV";
const int gprojid = 0x33;

enum NUM
{
    CREATER = 1,
    USER = 2,

    SIZE = 4096, // 共享内存的大小
};

class Shm
{
private:
    key_t GetKey()
    {
        key_t key = ftok(_pathname.c_str(), _projid);
        if (key < 0)
        {
            perror("ftok fail!");
            exit(1);
        }
        return key;
    }

    bool CreateShm()
    {
        if (_who == CREATER)
        {
            _shmid = shmget(_key, SIZE, IPC_CREAT | IPC_EXCL | 0666);
            if (_shmid < 0)
            {
                perror("shmget fail!");
                return false;
            }
            else
            // 创建成功
            {
                return true;
            }
        }
        else
            assert(false);
    }

    bool GetShm()
    {
        if (_who == USER)
        {
            _shmid = shmget(_key, SIZE, IPC_CREAT | 0666);
            if (_shmid < 0)
            {
                perror("shmget fail!");
                return false;
            }
            else
            // 创建成功
            {
                return true;
            }
        }
        else
            assert(false);
    }

public:
    Shm(const std::string &pathname, const int projid, int who) // const 只能在初始化列中初始化
        : _pathname(pathname), _projid(projid), _who(who), _addrshm(nullptr)
    {
        _key = GetKey();
        if (who == CREATER)
        // 第一次获取shm(创建一个全新的shm),若不是第一次，报错
        {
            if (CreateShm())
                cout << "CreateShm Success" << endl;
            else
                cerr << "CreateShm False" << endl;
        }
        else if (who == USER)
        // 获取一个shm
        {
            if (GetShm())
                cout << "GetShm Success" << endl;
            else
                cerr << "GetShm False" << endl;
        }
        else // 只有两种身份，不可能走到这里
        {
            cerr << "身份认证错误" << endl;
            assert(false);
        }

        _addrshm = ShmAttach(); // sever和client都需要attach
    }

    void *ShmAttach()
    {
        void *addr = shmat(_shmid, nullptr, 0);
        if (addr == ((void *)-1))
        {
            perror("shmat fail!");
            exit(1);
        }
        else
        {
            cout<<"Attach Success!"<<endl;
            return addr;
        }    
    }

    void ShmDetach(void *addrshm)
    {
        if (addrshm == nullptr)
            return;

        int res = shmdt(addrshm);
        if (res < 0)
        {
            perror("shmdt fail!");
            exit(1);
        }
        else
        {
            cout<<"Detach Success!"<<endl;
        }
    }

    void *Addr()
    {
        return _addrshm;
    }

    ~Shm()
    {
        //不论是server还是client都需要detach
        if (_addrshm != nullptr)
            ShmDetach(_addrshm);

        if (_who == CREATER)
        {
            int res = shmctl(_shmid, IPC_RMID, nullptr);
            if (res < 0)
            {
                perror("shmctl fail!");
                exit(1);
            }
            cout << "Shm Delete Success!" << endl;
        }
        else if (_who == USER)
        {
            cout << "Shm User Quit" << endl;
        }
        else
        {
            cerr << "身份认证错误" << endl;
            assert(false);
        }
    }

private:
    key_t _key; // 用户形成，内核使用的字段
    int _shmid; // 内核返回的标识符，用户进行shm管理

    const std::string _pathname;
    const int _projid;

    int _who; // 标识身份信息

    void *_addrshm; // shm的地址信息,在挂接的时候需要
};
